﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using space_matters.Utilities;

namespace space_matters
{
    abstract class Matter
    {
        #region Private Elements

        private double _radius;

        private Vector2 _position;

        private Vector2 _velocity;

        private Vector2 _tempForce;

        private float _maxVelocity;

        protected Animation Animation;

        protected Texture2D Texture;

        protected Texture2D TextureOverlay;

        #endregion

        public double TextureOverlayOpacity;

        Random rand = new Random();
        public float Rotation;

        #region Properties

        public Color Color { get; set; }
        public double Mass { get; set; }

        public double Radius
        { 
            get { return _radius; }

            set
            {
                //Texture = null;
                _radius = value;
            }
        }

        public Vector2 Pos
        {
            get { return _position; }
            
            set 
            { 
                _position = value;

                while (_position.X < 0)
                    _position.X += Game.WINDOW_WIDTH;

                while (_position.X > Game.WINDOW_WIDTH)
                    _position.X -= Game.WINDOW_WIDTH;

                while (_position.Y < 0)
                    _position.Y += Game.WINDOW_HEIGHT;

                while (_position.Y > Game.WINDOW_HEIGHT)
                    _position.Y -= Game.WINDOW_HEIGHT;
            }
        }

        public Vector2 Velocity
        {
            get { return _velocity; }

            set
            {
                _velocity = value;

                var len = value.Length();
                if (len > MaxVelocity)
                    _velocity *= MaxVelocity / len;
            }
        }

        public float MaxVelocity
        {
            get { return _maxVelocity; }

            set
            {
                _maxVelocity = value;
                Velocity = Velocity;
            }
        }

        #endregion

        #region Constructors

        protected Matter(Color color, double mass, double radius, Vector2 position)
            : this(color, mass, radius, position, Vector2.Zero)
        {
            Rotation = (float)(MathHelper.Pi * 2 * rand.NextDouble());
        }

        protected Matter(Color color, double mass, double radius, Vector2 position, Vector2 velocity, float maxVelocity = float.PositiveInfinity)
        {
            Color = color;
            Mass = mass;
            Radius = radius;
            Pos = position;
            MaxVelocity = maxVelocity;
            Velocity = velocity;

            Rotation = (float)rand.NextDouble() * (float)(MathHelper.Pi * 2);
        }

        #endregion

        public void ApplyForce(Vector2 force)
        {
            _tempForce += force;
        }

        #region Update Loop

        public virtual void Update(GameTime time)
        {
            Velocity += _tempForce * (float) time.ElapsedGameTime.TotalSeconds;
            Pos += Velocity * (float) time.ElapsedGameTime.TotalSeconds;
            _tempForce = Vector2.Zero;
        }

        #endregion

        #region Drawing Methods

        public virtual void Draw(SpriteBatch spriteBatch, GameTime time)
        {
            if (Texture == null)
            {
                Texture = DrawUtils.CreateCircle(spriteBatch.GraphicsDevice, (int)Radius);
            }

            var center = new Vector2(Texture.Width / 2f, Texture.Height / 2f);
            Rectangle? clip = null;

            float scale;
            if (Texture.Width > 2000)
                scale = (float)_radius / 30;
            else if (Texture.Width > 500)
                scale = (float)_radius / 16;
            else scale = (float) _radius/5;
            
            if (Animation != null)
            {
                Animation.Update(time);
                clip = Animation.CurrentFrame;
                center = new Vector2(clip.Value.Width / 2f, clip.Value.Height / 2f);
            }

            spriteBatch.Draw(Texture, Pos, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
            if (TextureOverlay != null)
                spriteBatch.Draw(TextureOverlay, Pos, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);

            // Now we need to handle wrapping.
            var horzBound = Texture.Width / 2f;
            var vertBound = Texture.Height / 2f;
            var horzOffset = new Vector2(Game.WINDOW_WIDTH, 0);
            var vertOffset = new Vector2(0, Game.WINDOW_HEIGHT);

            if (Pos.X < horzBound)
            {
                if (Pos.Y < vertBound)
                {
                    spriteBatch.Draw(Texture, Pos + horzOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos + vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos + horzOffset + vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos + horzOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos + vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos + horzOffset + vertOffset, clip, Color.White * (float)TextureOverlayOpacity, 0f, center, scale, SpriteEffects.None, 0.5f);
                    }
                }
                else if (Pos.Y > Game.WINDOW_HEIGHT - vertBound)
                {
                    spriteBatch.Draw(Texture, Pos + horzOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos - vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos + horzOffset - vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos + horzOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos - vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos + horzOffset - vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                    }
                }
                else
                {
                    spriteBatch.Draw(Texture, Pos + horzOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos + horzOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                    }
                }
            }
            else if (Pos.X > Game.WINDOW_WIDTH - horzBound)
            {
                if (Pos.Y < vertBound)
                {
                    spriteBatch.Draw(Texture, Pos - horzOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos + vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos - horzOffset + vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos - horzOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos + vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos - horzOffset + vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                    }
                }
                else if (Pos.Y > Game.WINDOW_HEIGHT - vertBound)
                {
                    spriteBatch.Draw(Texture, Pos - horzOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos - vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);
                    spriteBatch.Draw(Texture, Pos - horzOffset - vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos - horzOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos - vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                        spriteBatch.Draw(TextureOverlay, Pos - horzOffset - vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0.05f);
                    }
                }
                else
                {
                    spriteBatch.Draw(Texture, Pos - horzOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos - horzOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0f);
                    }
                }
            }
            else
            {
                if (Pos.Y < vertBound)
                {
                    spriteBatch.Draw(Texture, Pos + vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos + vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0f);
                    }
                }
                else if (Pos.Y > Game.WINDOW_HEIGHT - vertBound)
                {
                    spriteBatch.Draw(Texture, Pos - vertOffset, clip, Color, Rotation, center, scale, SpriteEffects.None, 0.1f);

                    if (TextureOverlay != null)
                    {
                        spriteBatch.Draw(TextureOverlay, Pos - vertOffset, clip, Color.White * (float)TextureOverlayOpacity, Rotation, center, scale, SpriteEffects.None, 0f);
                    }
                }
            }
        }

        #endregion
    }
}
